#pragma once

#include <iostream>
#include <string>

#include <cstring>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Protocol.hpp"
#include "log.hpp"

enum
{
    USAGE_ERR = 1,
    SOCK_ERR,
    CONNECT_ERR
};

namespace yzc_client
{
    using namespace std;
    class CalClient
    {
    public:
        CalClient(const string &ip, const uint16_t &port)
            : _sockfd(-1), _serverip(ip), _serverport(port) {}

        void InitClient()
        {
            // 1.创建套接字
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                cerr << "client creat socket error" << endl;
                exit(SOCK_ERR);
            }
            else
                cout << "client creat socket success" << endl;

            // 2. bind udp客户端不需要显示bind，tcp客户端也不需要显示bind
            // client的port需要让客户端自行选择

            // 3.客户端需不需要listen? 客户端不需要监听连接
            // 4.客户打需不需要accept? 不需要accept

            // 5. 客户端需要什么？ 需要发起连接
        }

        void start()
        {
            // 5. 创建通信结构体,并填入连接对方的信息。然后使用connect发起连接
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));

            server.sin_family = AF_INET;
            server.sin_port = htons(_serverport);
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());

            if (connect(_sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
            {
                cerr << "client connect error" << endl;
            }
            else
            {
                // 6.连接成功，可以通信
                string inbuffer;
                while (true)
                {
                    // 定义请求
                    Request req;
                    string msg{};
                    bool flag = true; // 表示输入的信息是否有误
                    while (true)
                    {
                        msg.clear();
                        std::cout << "my cal >>";
                        fflush(stdout);
                        getline(cin, msg);       // 会自动清空\n
                        if (ParseMsg(msg, &req)) // 说明成功定义请求
                            break;
                        else
                            continue;
                    }

                    // 发送请求
                    string context;
                    req.serialize(&context);                                   // 序列化数据
                    string send_string = enLength(context);                    // 添加报头
                    send(_sockfd, send_string.c_str(), send_string.size(), 0); // 有无bug？
                    if (errno == EPIPE)
                    {
                        std::cout << "server quit me too" << std::endl;
                        exit(errno);
                    }

                    // 读取响应字节流
                    // contex_len\r\nexitcode result\r\n   -> exitcode result
                    string response_str, response_text;
                    if (!recvPackage(_sockfd, inbuffer, &response_text)) // 读取响应
                        continue;
                    if (!deLength(response_text, &response_str)) // 删除报头
                        continue;

                    // 根据响应获取结果
                    Resopnse resp;
                    resp.deserialize(response_str); // 反序列化响应
                    cout << "exitcode:" << resp._exitcode << endl;
                    cout << "result:" << resp._result << endl;
                }
            }
        }

        ~CalClient()
        {
            if (_sockfd > 0)
                close(_sockfd);
        }

    private:
        bool ParseMsg(const string &msg, Request *rqs)
        {
            // msg 1+1 5*6 6/3 1203%4
            int cnt = 0; // 表示输入字符数字的数量，如果与msg.size()-1不相等，则说明输入错误
            string left, right;
            char op = ' ';

            for (const auto &c : msg)
            {
                // 找到字符了
                if (!isdigit(c))
                {
                    op = c;
                    if (op != '+' && op != '-' && op != '*' && op != '/' && op != '%')
                    {
                        cout << "输入的运算符错误,不能是:" << op << " 请重新输入\n";
                        return false;
                    }
                }
                else
                    cnt++;
            }
            if (cnt != msg.size() - 1)
            {
                cout << "没有输入操作符或者输入的操作符过多,请重新输入\n";
                return false;
            }

            auto pos = msg.find(op);
            if (pos == 0 || pos == msg.size() - 1)
            {
                cout << "输入的操作符不能在首尾,请重新输入\n";
                return false;
            }

            // 到这里，说明输入信息是正确的
            left = msg.substr(0, pos);
            right = msg.substr(pos + 1);

            rqs->_op = op;
            rqs->_x = stoi(left);
            rqs->_y = stoi(right);
            return true;
        }

    private:
        int _sockfd;          // 套接字
        string _serverip;     // 对方服务器ip
        uint16_t _serverport; // 对方服务器端口
    };
}